#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(const QString &classID, const int &numOfStu, QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , stuOfRemain(numOfStu)
{
    ui->setupUi(this);

    ui->pushButtonSure->setEnabled(false);

    srand(time(nullptr));

    // 测试数据库
    MySQL::getMySQL()->createTable();
    //MySQL::getMySQL()->insertStudent("1", "小红");

    // 根据人数实例化出控件
    loadSeats(numOfStu);

    // 加载摄像头
    loadVideo();

    // 开启定时器事件播放摄像头画面
    timer = startTimer(70);

    // 利用匿名分类器对象学习模型，之后赋值给我们的分类器
    classifier = cv::CascadeClassifier("D:/ACode/OPenCV/opencv3.4-install/install/etc/haarcascades/haarcascade_frontalface_alt2.xml");

    // 打开模型文件
    openFaceFile();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    qDebug() << "点击了关闭按钮";
    // 关闭定时器
    killTimer(timer);

    // 调用数据库清空操作删除数据
    MySQL::getMySQL()->deleteAllStudents();

    // 清空容器资源
    for(auto item : students){
        if(item)
            delete item;
    }

    // 释放资源
    vc.release();
    cv::destroyAllWindows();

    event->accept();
}

// 根据人数动态的加载座位
void MainWindow::loadSeats(int num)
{
    for(int i = 0; i < num; ++i){
        students.push_back(new Student());
        ui->gridLayout->addWidget(students.at(students.size() - 1)->getLabel(), i / 8, i % 8, 1, 1);
    }
}

// 加载摄像头
void MainWindow::loadVideo()
{
    vc.open(0);
}

void MainWindow::timerEvent(QTimerEvent *e)
{
    showVideo();
}

void MainWindow::showVideo()
{
    rects.clear();
    vc >> frame;

    cv::flip(frame, frame, 1);

    // 使用灰度图进行查找节省计算资源
    cv::Mat dst;
    cv::cvtColor(frame, dst, cv::COLOR_BGR2GRAY);
    // 在图中查找并将找到的数据放到容器中 参数是图片，容器，每次"盒"扩大倍数，比对数据数量
    // 内部会在图中扫描多次，每一次"盒"的放大都需要扫描一边图片
    classifier.detectMultiScale(dst, rects, 1.1, 3);

    // 在原图中绘制矩形 只绘制最大的脸
    if(rects.size() != 0){
        rectangle(frame, rects[rects.size() - 1], cv::Scalar(0, 255, 0), 2);
    }

    // mat to qimage
    QImage image(frame.data,
                 frame.cols,// 宽
                 frame.rows,// 高
                 frame.cols * frame.channels(),// 宽*通道计算一个像素点中小通道的总数
                 QImage::Format_BGR888// 图片类型(OpenCV的图片格式)
                 );

    // 转换成Pixmap
    QPixmap pix = QPixmap::fromImage(image);
    // 让图片和控件一样大
    pix = pix.scaled(QSize(ui->labelVideo->size()), Qt::KeepAspectRatio, Qt::SmoothTransformation);
    ui->labelVideo->setPixmap(pix);
}

// 获取随机数
int MainWindow::getRandomNum()
{
    return rand() % students.size();
}

// 打开人脸学习的模型xml文件
void MainWindow::openFaceFile()
{
    QFile file("face.xml");
    // 判断模型文件是否存在
    if(file.exists()){
        // 存在加载模型 使用LBPH算法
        recognizer = cv::face::FaceRecognizer::load<cv::face::LBPHFaceRecognizer>("face.xml");
        qDebug() << "xml读取成功";
    }else{
        // 不存在就创建
        recognizer = cv::face::LBPHFaceRecognizer::create();
        qDebug() << "xml文件创建成功";
    }
}

// 预测人脸，成功返回标签，没有找到就返回-1
int MainWindow::predictFaceIdentity()
{
    QFile file("face.xml");
    // 判断模型文件是否存在
    if(!file.exists()){
        return -1;
    }

    // 统一尺寸和转灰度
    cv::Mat dst;
    cvtColor(curFace, dst, cv::COLOR_BGR2GRAY);
    cv::resize(dst, dst, cv::Size(100, 100));

    // 做预测，得到标签和可信度
    int label;// 标签
    double confi;// 可信度
    recognizer->predict(dst, label, confi);
    qDebug() << "标签是" << label << "\t可信度是" << confi;

    if(confi >= 125){
        return -1;
    }else{
        return label;
    }
}

// 通过当前脸进行模型的学习
void MainWindow::konwYourFace(int id)
{
    if(predictFaceIdentity() != -1){
        // 已经存在不需要学习
        return;
    }

    std::vector<cv::Mat> faces;
    std::vector<int> labels;

    // 只能学习灰度图
    cv::Mat dst;
    cvtColor(curFace, dst, cv::COLOR_BGR2GRAY);
    // 统一尺寸
    cv::resize(dst, dst, cv::Size(100, 100));

    faces.push_back(dst);
    labels.push_back(id);

    // 人脸识别器更新
    recognizer->update(faces, labels);

    // 保存学习到的数据
    recognizer->save("face.xml");

    qDebug() << "学习了id为 " << id << " 的人脸";
}

// 通过id查找是否已经进入了教室
int MainWindow::findInRoom(QString id)
{
    for(auto item : students){
        if(item->getM_Name() == id){
            qDebug() << "找到id为 " << id << "的学生在教室里";
            return item->getM_Addr();
        }
    }

    qDebug() << "没有找到id为 " << id << "的学生在教室里";
    return -1;
}

// 通过位置离开教室
void MainWindow::leveClass(int addr)
{
    // 在数据库中插入离开时间
    MySQL::getMySQL()->insertLeveTime(students[addr]->getM_Name());

    // 更新作为信息
    QPixmap pix;
    pix.load(":/icon.png");
    pix = pix.scaled(QSize(60, 60));
    students[addr]->setImage(pix);
    students[addr]->loadLabelImage();
    students[addr]->setM_IsUsed(false);
    students[addr]->setM_Name("-1");
}

// 截图获取人脸识别的结果
void MainWindow::on_pushButtonFaceExtraction_clicked()
{
    ui->lineEditID->clear();
    ui->lineEditName->clear();

    if(rects.size() <= 0){
        return ;
    }

    // 获取到了当前人脸
    curFace = frame(rects[rects.size() - 1]);

    int label = -1;
    if((label = predictFaceIdentity()) != -1){
        // 在模型中找到了人脸

        QString lab = QString::number(label);

        ui->lineEditID->setText(lab);
        ui->lineEditName->setText(MySQL::getMySQL()->getNameById(lab));
    }

    // mat to qimage
    QImage image(curFace.data,
                 curFace.cols,// 宽
                 curFace.rows,// 高
                 static_cast<int>(curFace.step),// 宽*通道计算一个像素点中小通道的总数
                 QImage::Format_BGR888// 图片类型(OpenCV的图片格式)
                 );
    image = image.copy();

    // 转换成Pixmap
    pixFace = QPixmap::fromImage(image);
    // 让图片和控件一样大
    pixFace = pixFace.scaled(QSize(ui->labelFaceImage->size()), Qt::KeepAspectRatio, Qt::SmoothTransformation);
    ui->labelFaceImage->setPixmap(pixFace);

    ui->pushButtonSure->setEnabled(true);
}

// 确定按钮点击事件
void MainWindow::on_pushButtonSure_clicked()
{
    QString id = ui->lineEditID->text();
    QString name = ui->lineEditName->text();
    if(id == nullptr || name == nullptr){
        QMessageBox::warning(this, "注意", "请确保输入了正确的信息");
        return ;
    }

    // 先查找当前人像是否已经出现在当前教室
    int curId = -1;
    if((curId = findInRoom(id)) != -1){
        // 找到了当前学生，说明是离开教室
        leveClass(curId);
        QMessageBox::warning(this, "注意", "你已经成功离开教室");
        ui->pushButtonSure->setEnabled(false);
        return ;
    }

    if(!stuOfRemain){
        QMessageBox::warning(this, "注意", "教室已满");
        return;
    }

    int num = 0;
    do{
        num = getRandomNum() % 40;
    }while(students[num]->getM_IsUsed());

    students[num]->setImage(pixFace);
    students[num]->loadLabelImage();
    students[num]->setM_IsUsed(true);
    students[num]->setM_Name(id);
    students[num]->setM_Addr(num);

    // 学习人脸
    konwYourFace(id.toInt());

    // 数据库中插入数据
    MySQL::getMySQL()->insertStudent(id, name);

    stuOfRemain--;

    ui->pushButtonSure->setEnabled(false);
    QString msg = "您的座位是 %1 号";
    msg = msg.arg(num);
    QMessageBox::warning(this, "注意", msg);
}

// 点击导出按钮
void MainWindow::on_pushButtonSureLoadExcel_clicked()
{
    MySQL::getMySQL()->exportDataToExcel("D:\\Qt\\Workspace\\FaceRecognitionCheckInSystem\\build-FaceRecognitionCheckInSystem-Desktop_Qt_5_14_2_MinGW_32_bit-Debug\\test.xlsx");
}
